package impl

import (
	"context"
	"errors"
	"fmt"
	"github.com/starmotioncf/common/filter"
	"github.com/starmotioncf/common/log"
	"github.com/starmotioncf/common/redis"
	"github.com/starmotioncf/common/third_account"
	"github.com/starmotioncf/soutchatinternal"
	"github.com/starmotioncf/soutchatinternal/constant"
	"github.com/starmotioncf/soutchatinternal/constant/redis_key"
	"github.com/starmotioncf/soutchatinternal/dto"
	"github.com/starmotioncf/soutchatinternal/errcode"
	"github.com/starmotioncf/soutchatinternal/event"
	"github.com/starmotioncf/soutchatinternal/model"
	"github.com/starmotioncf/soutchatinternal/repository"
	"github.com/starmotioncf/soutchatinternal/service"
	"github.com/starmotioncf/soutchatpkg/account"
	"github.com/starmotioncf/soutchatpkg/jwt"
	"github.com/starmotioncf/soutchatpkg/setting"
	"go.uber.org/zap"
	"strings"
	"time"

	"github.com/gin-gonic/gin"
	"gorm.io/gorm"
)

type accountService struct {
	*service.Service
	jwt                  *jwt.JWT
	bruteForceLoginLimit *redis.RateLimiter
	thirdAccount         *account.ThirdAccount
	accountRepo          repository.AccountRepository
	userRepo             repository.UserRepository
	userIdRepo           repository.UserIdRepository
	countryRepo          repository.CountryRepository
	settingRepo          repository.SettingRepository
}

func NewAccountService(s *service.Service, jwt *jwt.JWT, thirdAccount *account.ThirdAccount, accountRepo repository.AccountRepository,
	userRepo repository.UserRepository, userIdRepo repository.UserIdRepository, countryRepo repository.CountryRepository,
	settingRepo repository.SettingRepository) service.AccountService {
	_s := &accountService{
		Service:      s,
		jwt:          jwt,
		thirdAccount: thirdAccount,
		accountRepo:  accountRepo,
		userRepo:     userRepo,
		userIdRepo:   userIdRepo,
		countryRepo:  countryRepo,
		settingRepo:  settingRepo,
	}
	//十分钟内错误登录20次，将会触发封禁
	//s.bruteForceLoginLimit = redis.NewRateLimit(redis.RateLimiterConfig{
	//	Limit:       20,
	//	LimitWindow: time.Minute * 5,
	//	Prefix:      "lrl:",
	//}, s.rdb)
	return _s
}

// LoginByThird 第三方登录
func (s *accountService) LoginByThird(ctx *gin.Context, req *dto.LoginByThirdReq) (*dto.LoginRes, error) {
	//校验token
	authPayload, err := s.thirdAccount.AuthToken(ctx, req.IdentityType, req.Credential)
	if err != nil {
		log.WithContext(ctx).Error("LoginByThird AuthToken err:", zap.Error(err))
		return nil, errcode.ErrThirdSDK
	}
	if authPayload == nil {
		return nil, errcode.ErrThirdSDK
	}
	//查询数据库登录表记录
	userAuth, err := s.accountRepo.GetAuthByIdentifier(ctx, authPayload.GetIdentType(), authPayload.GetIdentifier())
	if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
		return nil, err
	}
	country := s.getDefaultRegisterCountry(ctx)
	if country == nil {
		return nil, errors.New("country error")
	}

	accountSetting := s.getSetting(ctx, filter.EntityData{filter.Region: country.Region, filter.Country: country.Code})
	// 不存在，准备注册
	if userAuth == nil || userAuth.Uid == 0 {
		userAuth, err = s.createUser(ctx, accountSetting, authPayload, country)
		if err != nil {
			log.WithContext(ctx).Error("创建用户失败", zap.Error(err))
		}
	}
	//登录用户操作
	return s.loginUser(ctx, userAuth)
}

// LoginByPhoneAndPwd 手机密码登录
func (s *accountService) LoginByPhoneAndPwd(ctx *gin.Context, req *dto.LoginByPhoneAndPwdReq) (*dto.LoginRes, error) {
	userAuth, err := s.accountRepo.GetAuthByIdentifier(ctx, third_account.IdentityTypeMobilePhone, req.PhoneNumber)
	if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
		return nil, err
	}
	if userAuth == nil || userAuth.Uid == 0 {
		return nil, errcode.ErrUserInvalid
	}
	if userAuth.Credential != req.Password {
		return nil, errcode.ErrUserPwd
	}
	//登录用户操作
	return s.loginUser(ctx, userAuth)
}

// LoginByPhoneAndCaptcha 手机验证码登录
func (s *accountService) LoginByPhoneAndCaptcha(ctx *gin.Context, req *dto.LoginByPhoneAndCaptchaReq) (*dto.LoginRes, error) {
	return nil, errcode.ErrAlert.New("not support")
}

// RefreshToken 刷新token
func (s *accountService) RefreshToken(ctx *gin.Context, req *dto.AccountRefreshTokenReq) (*dto.LoginRes, error) {
	return nil, nil
}

func (s *accountService) GetProfile(ctx *gin.Context) (*dto.AccountProfile, error) {
	uid := internal.GetUidFromCtx(ctx)
	if uid == 0 {
		return nil, errcode.ErrUserInvalid
	}
	userInfo, err := s.userRepo.Get(ctx, uid)
	if err != nil {
		return nil, err
	}
	return &dto.AccountProfile{
		Uid:         uid,
		Duid:        userInfo.Duid,
		Nickname:    userInfo.Nickname,
		Avatar:      userInfo.Avatar,
		Region:      userInfo.Region,
		Gender:      userInfo.Gender,
		CountryCode: userInfo.CountryCode,
		Gold:        userInfo.Gold,
	}, nil
}

func (s *accountService) CheckMobilePhone(ctx *gin.Context, req *dto.MobilePhone) (*dto.CheckMobilePhoneRes, error) {
	auth, err := s.accountRepo.GetAuthByIdentifier(ctx, third_account.IdentityTypeMobilePhone, req.PhoneNumber)
	if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
		return nil, err
	}
	res := &dto.CheckMobilePhoneRes{}
	if auth != nil && auth.Uid > 0 {
		res.Registered = true //已被注册
	}
	countryInfo, err := s.countryRepo.GetByArea(ctx, req.Area)
	if err != nil {
		return nil, err
	}
	if countryInfo != nil && countryInfo.SmsProvider > 0 {
		res.SmsSupported = true //支持短信
	}
	return res, nil
}

// loginUser
func (s *accountService) loginUser(ctx *gin.Context, userAuth *model.UserAuth) (_ *dto.LoginRes, err error) {
	userModel, err := s.userRepo.Get(ctx, userAuth.Uid)
	if err != nil {
		return nil, err
	}
	tp := jwt.TokenPayload{
		Uid:    userModel.Uid,
		Region: userModel.Region,
	}
	//	获取用户基本信息
	userInfo := dto.LoginRes{
		Uid:    userModel.Uid,
		Region: userModel.Region,
	}
	userInfo.Token, userInfo.ExpiredAt, err = s.jwt.CreateToken(tp)
	if err != nil {
		return
	}
	userInfo.RefreshToken, userInfo.ExpiredAtRefreshToken, err = s.jwt.CreateToken(tp)
	if err != nil {
		return
	}
	//app 登录， 生成融云token
	//if loginType == constant.LoginTypeApp {
	//	rongcloudToken := s.rc.GetToken(userInfo.Uid, userInfo.Nickname, userInfo.Avatarurl, func(uid uint64, token string) {})
	//	updateUser["rongcloudtoken"] = rongcloudToken
	//	updateUser["accesstoken"] = userInfo.AccessToken
	//	userInfo.Rongcloudtoken = rongcloudToken
	//}
	//if header.GetPlatform() == int64(constant.PlatformIos) {
	//	updateUser["versionios"] = header.GetVersion()
	//} else {
	//	updateUser["version"], _ = strconv.Atoi(header.GetVersion())
	//}
	//if _err := s.userRepo.UpdateFields(ctx, userInfo.Uid, updateUser); _err != nil {
	//	logs.Error("更新用户表信息失败", _err)
	//}
	//更新用户表, 这里后续可以不要了，目前为了兼容旧逻辑 --end

	//更新auth表
	err = s.accountRepo.LoginAuth(ctx, userAuth, userInfo.Token, userInfo.ExpiredAt)
	return &userInfo, err
}

// createUser 创建新用户
func (s *accountService) createUser(ctx *gin.Context, accountSetting setting.Account, authPayload third_account.AuthPayload, countryInfo *model.Country) (userAuth *model.UserAuth, err error) {
	if countryInfo == nil {
		err = errors.New("country is empty")
		return nil, err
	}
	//加并发锁控制
	locker := redis.NewLocker(redis_key.AccountRegisterLock.ToString(), s.RDB())
	if _err := locker.TryLock(time.Second * 3); _err != nil {
		return nil, errors.New("service busy")
	}
	defer locker.UnLock()
	// 检查手机登录时的密码不能为空
	if authPayload.GetIdentType() == third_account.IdentityTypeMobilePhone && authPayload.GetCredential() == "" {
		err = errors.New("password is empty")
		return nil, err
	}
	user := model.User{
		Birthday:    "2000-01-01",
		Avatar:      accountSetting.DefaultAvatar.Male,
		Region:      countryInfo.Region,
		CountryCode: countryInfo.Code,
		Gender:      0,
		Nickname:    authPayload.GetNickname(),
		CreatedAt:   time.Now().Unix(), //注册时间
		UpdatedAt:   time.Now().Unix(),
	}
	//从数据库获取一个uid
	user.Uid, err = s.userIdRepo.GetOne(ctx)
	if err != nil {
		return
	}
	if user.Uid == 0 {
		err = errcode.ErrUnknown
		return
	}
	user.Duid = fmt.Sprintf("%d", user.Uid)

	// 补充信息
	if user.Nickname == "" || len(user.Nickname) > 30 {
		user.Nickname = fmt.Sprintf("user%v", user.Uid)
	}

	userAuth = &model.UserAuth{
		Uid:          user.Uid,
		IdentityType: authPayload.GetIdentType(),
		Identifier:   authPayload.GetIdentifier(),
		Credential:   authPayload.GetCredential(),
		Account:      authPayload.GetAccount(),
		Nickname:     authPayload.GetNickname(),
		Binding:      0,
		CreatedAt:    user.CreatedAt,
		UpdatedAt:    user.UpdatedAt,
	}
	//插入新用户到数据库
	err = s.Transaction.Transaction(ctx, func(ctx context.Context) error {
		//user表
		if err = s.userRepo.Create(ctx, &user); err != nil {
			return err
		}
		//更新已经使用的user_id
		if err = s.userIdRepo.TakeOne(ctx, user.Uid); err != nil {
			return err
		}
		//插入第三方关系表
		if err = s.accountRepo.CreateAuth(ctx, userAuth); err != nil {
			return err
		}
		//创建扩展信息
		//if err = s.userRepo.CreateExtend(ctx, &model.VcUserExtend{Uid: user.Uid, Region: country.Region, LastModifyCountry: time.Now().Unix()}); err != nil {
		//	return err
		//}
		return nil
	})
	if err != nil {
		return nil, err
	}
	//注册成功后的事件推送
	s.PublishAsync(event.AccountRegisterEvent, event.AccountRegisterEventPayload{
		Time: time.Now(),
		Uid:  user.Uid,
		Auth: userAuth,
	})
	return userAuth, nil
}

// getDefaultRegisterCountry 注册时获取默认的国家
func (s *accountService) getDefaultRegisterCountry(ctx *gin.Context) (country *model.Country) {
	country, _ = s.countryRepo.Get(ctx, strings.ToUpper(ctx.Request.Header.Get(constant.CFIPCountry)))
	//根据语言获取
	//lang := strings.Replace(context2.NewApiHeader(req).GetSysLang(), "_", "-", -1)
	//s.countryRepo.ForEach(ctx, func(m *model.VcCountry) error {
	//	if m.Lang == lang && m.Hidden == 1 {
	//		country = m
	//		return io.EOF
	//	}
	//	return nil
	//})
	//if country != nil {
	//	return
	//}
	////以前的逻辑
	//langShort := utils.GetLang(lang)
	//s.countryRepo.ForEach(ctx, func(m *model.VcCountry) error {
	//	if strings.Index(m.Lang, langShort) != -1 && m.Hidden == 1 {
	//		country = m
	//		return io.EOF
	//	}
	//	return nil
	//})
	//if country != nil {
	//	return
	//}
	//country, _ = s.countryRepo.Get(ctx, utils.GetCFDefaultCountryId(req))
	if country == nil {
		country, _ = s.countryRepo.Get(ctx, "TR")
	}
	return
}

func (s *accountService) getSetting(ctx context.Context, ent filter.Entity) setting.Account {
	res, ok := s.settingRepo.Get(ctx, "service.account", ent).(setting.Account)
	if !ok {
		log.WithContext(ctx).Error("未找到[service.account]的配置")
	}
	return res
}
